/*
* Copyright (C) 2012-2014 qar
* License: http://www.gnu.org/licenses/gpl.html GPL version 2
*/

#ifndef ENCRYPTEDDEVICE_H
#define ENCRYPTEDDEVICE_H

#include <QtCore/QIODevice>

#include "crypto/hasher.h"
#include "crypto/cipher.h"
#include "crypto/authenticator.h"

class EncryptedDevice : public QIODevice
{
    Q_OBJECT

public:
    // Creates an EncryptedDevice on top of an existing device (e.g. a QFile).
    EncryptedDevice(QIODevice* rawDevice, QObject* parent = 0);

    virtual ~EncryptedDevice();

    // Opens the encrypted device.
    // If the raw device is not open, it will be opened using the same mode.
    // If the raw device is open, it must have the same open mode.
    // The flags QIODevice::Append and QIODevice::Text are forbidden.
    // Returns true for success.
    virtual bool open(OpenMode mode,
                      Hasher::IntegrityAlgorithm integrityAlgorithm = Hasher::Sha1,
                      Cipher::EncryptionAlgorithm encryptionAlgorithm = Cipher::NoEncryption,
                      Authenticator::AuthenticationAlgorithm authenticationAlgorithm = Authenticator::NoAuthentication,
                      QString keyFile = QString());

    // Closes the encrypted device (not the raw device). Writes any buffered data and the ending sequence.
    virtual void close();

    // Returns true. Seeking is not supported.
    virtual bool isSequential() const;

    // Returns the position in the cleartext stream.
    virtual qint64 pos() const;

    // Returns bytesAvailable(), NOT the size of the cleartext stream.
    virtual qint64 size() const;

    // Seeking is not supported. Does nothing and returns false.
    virtual bool seek(qint64 pos);

    // Returns true if there is no more cleartext data to be read. The rawDevice might still have data though.
    virtual bool atEnd() const;

    // Reset is not supported. Does nothing and returns false.
    virtual bool reset();

    // Returns the number of bytes that can be read immediately.
    virtual qint64 bytesAvailable() const;

    // Returns zero.
    virtual qint64 bytesToWrite() const;

protected:
    virtual qint64 writeData(const char* data, qint64 size);
    virtual qint64 readData(char* data, qint64 size);

    bool writeSegment(QByteArray segment);
    bool readSegment();

    qint64 writeRawData(QByteArray data);
    QByteArray readRawData(int len);

    qint64 integrityHashSize() const;
    qint64 encryptionBlockSize() const;
    qint64 authenticationHashSize() const;
    qint64 maxClearSegmentSize() const;
    qint64 maxEncryptedSegmentSize() const;
    qint64 rawBlockSize() const;

    static qint32 minVersion();
    static qint32 maxVersion();

private:
    QIODevice* rawDevice;
    qint32 version;
    QByteArray clearBuffer;
    qint64 clearTextPos;
    bool reachedEnd;
    Hasher integrityHasher;
    Cipher cipher;
    Authenticator authenticator;
};

#endif // ENCRYPTEDDEVICE_H
